package com.zengqingfa.springboot.redis.rest;

import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Map;

/**
 * @author zengqingfa
 * @className ThreadLocalController
 * @description
 * @create 2022/9/23 21:02
 */
@RestController
@RequestMapping("/threadLocal")
@Slf4j
public class ThreadLocalController {

    private final ThreadLocal<String> currentUser = ThreadLocal.withInitial(() -> null);

    /**
     * # 正常情况下应该
     * {
     *     "1": {
     *         "before": "http-nio-8087-exec-7:null",
     *         "after": "http-nio-8087-exec-7:0:0:0:0:0:0:0:1"
     *     }
     * }
     * <p>
     * # 多刷新几次 当线程池中的线程全部调用过，他们之前获得的信息没有被清理
     * {
     *     "1": {
     *         "before": "http-nio-8087-exec-8:0:0:0:0:0:0:0:1",
     *         "after": "http-nio-8087-exec-8:0:0:0:0:0:0:0:1"
     *     }
     * }
     *
     * @return
     */
    @GetMapping("/wrong")
    public JSONObject wrong() {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        String ipAddr = getIpAddr(request);
        //设置用户信息之前先查询一次ThreadLocal中的用户信息
        String before = Thread.currentThread().getName() + ":" + currentUser.get();
        //设置用户信息到ThreadLocal
        currentUser.set(ipAddr);
        JSONObject jsonObject = new JSONObject();
        //设置用户信息之后再查询一次ThreadLocal中的用户信息
        String after = Thread.currentThread().getName() + ":" + currentUser.get();
        //汇总输出两次查询结果
        Map<String, String> result = new HashMap();
        result.put("before", before);
        result.put("after", after);
        jsonObject.put("1", result);
        return jsonObject;
    }

    /**
     * 获取Ip地址的方法
     *
     * @param request
     * @return
     */
    public static String getIpAddr(HttpServletRequest request) {
        String ipAddress = null;
        try {
            ipAddress = request.getHeader("x-forwarded-for");
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("Proxy-Client-IP");
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("WL-Proxy-Client-IP");
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getRemoteAddr();
                if (ipAddress.equals("127.0.0.1")) {
                    // 根据网卡取本机配置的IP
                    InetAddress inet = null;
                    try {
                        inet = InetAddress.getLocalHost();
                    } catch (UnknownHostException e) {
                        e.printStackTrace();
                    }
                    ipAddress = inet.getHostAddress();
                }
            }
            // 对于通过多个代理的情况，第一个IP为客户端真实IP,多个IP按照','分割
            if (ipAddress != null && ipAddress.length() > 15) { // "***.***.***.***".length()
                // = 15
                if (ipAddress.indexOf(",") > 0) {
                    ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
                }
            }
        } catch (Exception e) {
            ipAddress = "";
        }
        // ipAddress = this.getRequest().getRemoteAddr();
        return ipAddress;
    }


    /**
     * # 不管如何测试都是不会拿到上一个被调用线程时的ip地址
     * {
     *     "1": {
     *         "before": "http-nio-8087-exec-9:null",
     *         "after": "http-nio-8087-exec-9:0:0:0:0:0:0:0:1"
     *     }
     * }
     *
     * @return
     */
    @GetMapping("/wrong2")
    public JSONObject wrong2() {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        String ipAddr = getIpAddr(request);
        //设置用户信息之前先查询一次ThreadLocal中的用户信息
        String before = Thread.currentThread().getName() + ":" + currentUser.get();
        //设置用户信息到ThreadLocal
        currentUser.set(ipAddr);
        JSONObject jsonObject = new JSONObject();
        try {
            //设置用户信息之后再查询一次ThreadLocal中的用户信息
            String after = Thread.currentThread().getName() + ":" + currentUser.get();
            //汇总输出两次查询结果
            Map result = new HashMap();
            result.put("before", before);
            result.put("after", after);
            jsonObject.put("1", result);
        } finally {
            currentUser.remove();
        }
        return jsonObject;
    }

    /**
     * # 不管如何测试都是不会拿到上一个被调用线程时的ip地址
     * {
     *     "1": {
     *         "before": "http-nio-8087-exec-6:0:0:0:0:0:0:0:1",
     *         "after": "http-nio-8087-exec-6:0:0:0:0:0:0:0:1"
     *     }
     * }
     *
     * @return
     */
    @GetMapping("/wrong3")
    public JSONObject wrong3() {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        String ipAddr = getIpAddr(request);
        //设置用户信息之前先查询一次ThreadLocal中的用户信息
        String before = Thread.currentThread().getName() + ":" + currentUser.get();
        //使用之前先删除
        currentUser.remove();
        //设置用户信息到ThreadLocal
        currentUser.set(ipAddr);
        JSONObject jsonObject = new JSONObject();
        //设置用户信息之后再查询一次ThreadLocal中的用户信息
        String after = Thread.currentThread().getName() + ":" + currentUser.get();
        //汇总输出两次查询结果
        Map result = new HashMap();
        result.put("before", before);
        result.put("after", after);
        jsonObject.put("1", result);
        return jsonObject;
    }
}